package org.jeecg.netty.sensor.util;

public class CRCUtils {

	public static String Make_CRC_No_BYTE(byte[] b) {
		byte[] buf = new byte[b.length - 3];// 存储需要产生校验码的数据
		for (int i = 0; i < b.length - 3; i++) {
			buf[i] = b[i];
		}
		int len = buf.length;
		int crc = 0xFFFF;
		for (int pos = 0; pos < len; pos++) {
			if (buf[pos] < 0) {
				crc ^= (int) buf[pos] + 256; // XOR byte into least sig. byte of
												// crc
			} else {
				crc ^= (int) buf[pos]; // XOR byte into least sig. byte of crc
			}
			for (int i = 8; i != 0; i--) { // Loop over each bit
				if ((crc & 0x0001) != 0) { // If the LSB is set
					crc >>= 1; // Shift right and XOR 0xA001
					crc ^= 0xA001;
				} else
					// Else LSB is not set
					crc >>= 1; // Just shift right
			}
		}
		String c = Integer.toHexString(crc);
		if (c.length() == 4) {
			c = c.substring(0, 2) + c.substring(2, 4);
		} else if (c.length() == 3) {
			c = "0" + c;
			c = c.substring(0, 2) + c.substring(2, 4);
		} else if (c.length() == 2) {
			c = "00" + c;
		} else if (c.length() == 1) {
			c = "000" + c;
		}

		byte[] byteArray = c.getBytes();
		return c;
	}

	public static String getCRC(byte[] calcdata) {
//      ModBus 通信协议的 CRC ( 冗余循环校验码含2个字节, 即 16 位二进制数。
//      CRC 码由发送设备计算, 放置于所发送信息帧的尾部。
//      接收信息设备再重新计算所接收信息 (除 CRC 之外的部分）的 CRC,
//      比较计算得到的 CRC 是否与接收到CRC相符, 如果两者不相符, 则认为数据出错。
//
//      1) 预置 1 个 16 位的寄存器为十六进制FFFF(即全为 1) , 称此寄存器为 CRC寄存器。
//      2) 把第一个 8 位二进制数据 (通信信息帧的第一个字节) 与 16 位的 CRC寄存器的低 8 位相异或, 把结果放于 CRC寄存器。
//      3) 把 CRC 寄存器的内容右移一位( 朝低位)用 0 填补最高位, 并检查右移后的移出位。
//      4) 如果移出位为 0, 重复第 3 步 ( 再次右移一位); 如果移出位为 1, CRC 寄存器与多项式A001 ( 1010 0000 0000 0001) 进行异或。
//      5) 重复步骤 3 和步骤 4, 直到右移 8 次,这样整个8位数据全部进行了处理。
//      6) 重复步骤 2 到步骤 5, 进行通信信息帧下一个字节的处理。
//      7) 将该通信信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低字节进行交换。
//      8) 最后得到的 CRC寄存器内容即为 CRC码。

      int CRC = 0x0000ffff;
      int POLYNOMIAL = 0x0000a001;

      int i, j;
      for (i = 0; i < calcdata.length; i++) {
          CRC ^= (int) calcdata[i];
          for (j = 0; j < 8; j++) {
              if ((CRC & 0x00000001) == 1) {
                  CRC >>= 1;
                  CRC ^= POLYNOMIAL;
              } else {
                  CRC >>= 1;
              }
          }
      }
      //高低位转换，看情况使用（譬如本人这次对led彩屏的通讯开发就规定校验码高位在前低位在后，也就不需要转换高低位)
      //CRC = ( (CRC & 0x0000FF00) >> 8) | ( (CRC & 0x000000FF ) << 8);
      return Integer.toHexString(CRC);
	}

	/**
	 * 计算产生校验码
	 *
	 * @param data 需要校验的数据
	 * @return
	 */
	public static String Make_CRC(byte[] b) {
		byte[] buf = new byte[b.length];// 存储需要产生校验码的数据
		for (int i = 0; i < b.length; i++) {
			buf[i] = b[i];
		}
		int len = buf.length;
		int crc = 0xFFFF;
		for (int pos = 0; pos < len; pos++) {
			if (buf[pos] < 0) {
				crc ^= (int) buf[pos] + 256; // XOR byte into least sig. byte of
												// crc
			} else {
				crc ^= (int) buf[pos]; // XOR byte into least sig. byte of crc
			}
			for (int i = 8; i != 0; i--) { // Loop over each bit
				if ((crc & 0x0001) != 0) { // If the LSB is set
					crc >>= 1; // Shift right and XOR 0xA001
					crc ^= 0xA001;
				} else
					// Else LSB is not set
					crc >>= 1; // Just shift right
			}
		}
		String c = Integer.toHexString(crc);
		if (c.length() == 4) {
			c = c.substring(0, 2) + c.substring(2, 4);
		} else if (c.length() == 3) {
			c = "0" + c;
			c = c.substring(0, 2) + c.substring(2, 4);
		} else if (c.length() == 2) {
			c = "0" + c.substring(1, 2) + "0" + c.substring(0, 1);
		}
		// System.out.println(c);
		return HexConvert.byteArrayToHexString(b) + c;
	}

	/**
	 * CRC-16 (Modbus)
	 * CRC16_MODBUS：多项式x16+x15+x2+1（0x8005），初始值0xFFFF，低位在前，高位在后，结果与0x0000异或
	 * 0xA001是0x8005按位颠倒后的结果
	 *
	 * @param buffer
	 * @return
	 */
	public static int CRC16_MODBUS(byte[] buffer) {
		int wCRCin = 0xffff;
		int POLYNOMIAL = 0xa001;
		for (byte b : buffer) {
			wCRCin ^= ((int) b & 0x00ff);
			for (int j = 0; j < 8; j++) {
				if ((wCRCin & 0x0001) != 0) {
					wCRCin >>= 1;
					wCRCin ^= POLYNOMIAL;
				} else {
					wCRCin >>= 1;
				}
			}
		}
		return wCRCin ^= 0x0000;
	}

	/**
	 * CRC-CCITT(Kermit)验证模式
	 *
	 * @param str
	 * @return
	 */
	public static String CRC_CCITT_Kermit(String str) {
		int j, b, rrrc, c, i;
		String tmpBalance;
		int k;
		rrrc = 0;
		tmpBalance = str;
		int tmpInt, CharInt;
		String tmpChar, tmpStr;
		tmpStr = "";
		int High;
		int Low;

		for (j = 1; j <= 3; j++) {

			if (Character.isDigit(tmpBalance.charAt(2 * j - 2))) {
				High = Integer.parseInt(tmpBalance.charAt(2 * j - 2) + "");

			} else {
				High = 0;

			}
			if (Character.isDigit(tmpBalance.charAt(2 * j - 1))) {
				Low = Integer.parseInt(tmpBalance.charAt(2 * j - 1) + "");

			} else {

				Low = 0;
			}

			High = (High & 0xff) << 4;

			High = High | Low;

			k = High;

			for (i = 1; i <= 8; i++) {
				c = rrrc & 1;
				rrrc = rrrc >> 1;
				if ((k & 1) != 0) {
					rrrc = rrrc | 0x8000;

				}
				if (c != 0) {

					rrrc = rrrc ^ 0x8408;
				}

				k = k >> 1;
			}
		}
		for (i = 1; i <= 16; i++) {
			c = rrrc & 1;
			rrrc = rrrc >> 1;

			if (c != 0) {

				rrrc = rrrc ^ 0x8408;
			}

		}
		c = rrrc >> 8;
		b = rrrc << 8;
		rrrc = c | b;
		tmpInt = rrrc;
		tmpStr = "";
		for (i = 1; i <= 4; i++) {
			tmpChar = "";
			CharInt = tmpInt % 16;
			if (CharInt > 9) {
				switch (CharInt) {
				case 10:
					tmpChar = "A";
					break;
				case 11:

					tmpChar = "B";
					break;
				case 12:
					tmpChar = "C";
					break;

				case 13:

					tmpChar = "D";
					break;

				case 14:

					tmpChar = "E";
					break;

				case 15:

					tmpChar = "F";
					break;

				}
			} else {

				tmpChar = Integer.toString(CharInt);
			}

			tmpInt = tmpInt / 16;
			tmpStr = tmpChar + tmpStr;

		}

		System.out.println("tmpStr:" + tmpStr);

		return tmpStr;

	}

	/**
	 * CRC-CCITT(XModem) CRC-CCITT(0xFFFF) CRC-CCITT(0x1D0F) 校验模式
	 *
	 * @param flag < XModem(flag=1) 0xFFFF(flag=2) 0x1D0F(flag=3)>
	 * @param str
	 * @return
	 */
	public static String CRC_CCITT(int flag, String str) {
		int crc = 0x00; // initial value
		int polynomial = 0x1021;
		byte[] bytes = str.getBytes();

		switch (flag) {
		case 1:
			crc = 0x00;
			break;
		case 2:
			crc = 0xFFFF;
			break;
		case 3:
			crc = 0x1D0F;
			break;

		}
		for (int index = 0; index < bytes.length; index++) {
			byte b = bytes[index];
			for (int i = 0; i < 8; i++) {
				boolean bit = ((b >> (7 - i) & 1) == 1);
				boolean c15 = ((crc >> 15 & 1) == 1);
				crc <<= 1;
				if (c15 ^ bit)
					crc ^= polynomial;
			}
		}
		crc &= 0xffff;
		str = Integer.toHexString(crc);
		return str;

	}

	/**
	 * 计算CRC16校验码
	 *
	 * @param bytes 字节数组
	 * @return {@link String} 校验码
	 * @since 1.0
	 */
	public static String CRC_CHECKOUT(byte[] bytes) {
		int CRC = 0x0000ffff;
		int POLYNOMIAL = 0x0000a001;
		int i, j;
		for (i = 0; i < bytes.length; i++) {
			CRC ^= ((int) bytes[i] & 0x000000ff);
			for (j = 0; j < 8; j++) {
				if ((CRC & 0x00000001) != 0) {
					CRC >>= 1;
					CRC ^= POLYNOMIAL;
				} else {
					CRC >>= 1;
				}
			}
		}
		return Integer.toHexString(CRC);
	}

	/**
	 * 计算CRC16校验码
	 *
	 * @param bytes 字节数组
	 * @return {@link String} 校验码
	 * @since 1.0
	 */
	public static String CRC_CHECKOUT_SUM(byte[] bytes) {
		int CRC = 0x0000;
		String value = "";
		for (int i = 0; i < bytes.length; i++) {
			CRC += ((int) bytes[i] & 0xff);
		}
		String str = Integer.toHexString(CRC);
		int mLength = 4 - str.length();

		if (mLength > 0) {
			for (int j = 0; j < mLength; j++) {
				value = value + "0";
			}
		}
		value = value + str;
		return value;
	}

	public static String makeChecksum(String data) {
		if (data == null || data.equals("")) {
			return "";
		}
		int total = 0;
		int len = data.length();
		int num = 0;
		while (num < len) {
			String s = data.substring(num, num + 2);
			total += Integer.parseInt(s, 16);
			num = num + 2;
		}
		/**
		 * 用256求余最大是255，即16进制的FF
		 */
		// int mod = total % 256;
		String hex = Integer.toHexString(total);
		len = hex.length();
		// 如果不够校验位的长度，补0,这里用的是两位校验
		if (len < 4) {
			hex = "0" + hex;
		}
		return hex;

	}

}
